﻿/*
 * Author: Mattan360
 * Contributors:  VillageIdiot
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Irrlicht.Net.Video
{
    public class Color
    {       
        internal uint color;

        public Color(uint alpha, uint red, uint green, uint blue)
        {
            color = (((alpha & 0xff) << 24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff));
        }

        public Color(uint color)
        {
            this.color = color;
        }

        public uint Alpha 
        {
            get
            { 
                return color >> 24; 
            }

            set 
            {
                color = ((value & 0xff)<<24) | (color & 0x00ffffff); 
            }
        }

        public uint Red 
        { 
            get 
            {
                return (color>>16) & 0xff; 
            }
            
            set
            {
                color = ((value & 0xff)<<16) | (color & 0xff00ffff); 
            } 
        }

        public uint Green 
        {
            get
            {
                return (color>>8) & 0xff;
            }
            
            set
            {
                color = ((value & 0xff)<<8) | (color & 0xffff00ff); 
            }
        }

        public uint Blue
        {
            get
            {
                return color & 0xff; 
            }
            
            set
            {
                color = (value & 0xff) | (color & 0xffffff00); 
            }
        }

        public uint ColorValue
        {
            get
            {
                return color;
            }

            set
            {
                color = value;
            }
        }
                         
        public float Lightness
        {
            get
            {           
                return 0.5F * (Math.Max(Math.Max(Red, Green), Blue) + Math.Min(Math.Min(Red, Green), Blue));
            }
        }
                 
        public float Luminance
        {
            get
            {         
                return (0.3F * Red) + (0.59F * Green) + (0.11F * Blue);
            }
        }
                  
        public uint Average 
        {
            get
            {
                return (Red + Green + Blue) / 3;
            }
        }
                  
        public static bool operator == (Color color1, Color color2) 
        {
            return color2.ColorValue == color1.ColorValue; 
        }
  
        public static bool operator != (Color color1, Color color2)
        {
            return color2.ColorValue != color1.ColorValue; 
        }
 
        public static bool operator < (Color color1, Color color2)
        {
            return (color1.ColorValue < color2.ColorValue); 
        }

        public static bool operator > (Color color1, Color color2)
        {
            return (color1.ColorValue > color2.ColorValue);
        }
                  
        public static Color operator + (Color color1, Color color2) 
        {
            return new Color(Math.Min(color1.Alpha + color2.Alpha, 255u),
                Math.Min(color1.Red + color2.Red, 255u),                
                Math.Min(color1.Green + color2.Green, 255u),
                Math.Min(color1.Blue + color2.Blue, 255u));                 
        }

        public override bool Equals(object obj)
        {
            if (obj is Color)
            {
                return (this == (Color)obj);
            }
            else
            {
                return false;
            }
        }

        public override int GetHashCode()
        {
            return (int)(this.color);
        }

        public static ushort RGBA16(uint r, uint g, uint b, uint a = 0xFF)
        {                        
            return (ushort)((a & 0x80) << 8 |                
                (r & 0xF8) << 7 |                
                (g & 0xF8) << 2 |                
                (b & 0xF8) >> 3);
        }

        public static ushort RGB16(uint r, uint g, uint b)
        {
            return RGBA16(r, g, b);
        }

        public static ushort RGB16from16(ushort r, ushort g, ushort b)
        {
            return (ushort)(0x8000 | (r & 0x001F) << 10 | (g & 0x001F) << 5 | (b & 0x001F));
        }

        public static ushort X8R8G8B8toA1R5G5B5(uint color)
        {
            return (ushort)(0x8000 |
                (color & 0x00F80000) >> 9 |
                (color & 0x0000F800) >> 6 |
                (color & 0x000000F8) >> 3);
        }

        public static ushort A8R8G8B8toA1R5G5B5(uint color)
        {
            return (ushort)((color & 0x80000000) >> 16 |
                (color & 0x00F80000) >> 9 |
                (color & 0x0000F800) >> 6 |
                (color & 0x000000F8) >> 3);
        }

        public static ushort A8R8G8B8toR5G6B5(uint color)
        {
            return (ushort)((color & 0x00F80000) >> 8 |
                (color & 0x0000FC00) >> 5 |
                (color & 0x000000F8) >> 3);
        }

        public static uint A1R5G5B5toA8R8G8B8(ushort color)
        {
            return (uint)(((-((int)color & 0x00008000U) >> (int)31) & 0xFF000000U) |
                ((color & 0x00007C00U) << 9) | ((color & 0x00007000U) << 4) |
                ((color & 0x000003E0U) << 6) | ((color & 0x00000380U) << 1) |
                ((color & 0x0000001FU) << 3) | ((color & 0x0000001CU) >> 2)
                );
        }

        public static uint R5G6B5toA8R8G8B8(ushort color)
        {
            return (uint)(0xFF000000 |
                ((color & 0xF800U) << 8) |
                ((color & 0x07E0U) << 5) |
                ((color & 0x001FU) << 3));
        }

        public static ushort R5G6B5toA1R5G5B5(ushort color)
        {
            return (ushort)(0x8000 | (((color & 0xFFC0) >> 1) | (color & 0x001F)));
        }

        public static ushort A1R5G5B5toR5G6B5(ushort color)
        {
            return (ushort)(((color & 0x7FE0) << 1) | (color & 0x001F));
        }

        public static uint GetAlpha(ushort color)
        {
            return (uint)((color >> 15) & 0x0001);
        }

        public static uint GetRed(ushort color)
        {
            return (uint)((color >> 10) & 0x001F);
        }

        public static uint GetGreen(ushort color)
        {
            return (uint)((color >> 5) & 0x001F);
        }

        public static uint GetBlue(ushort color)
        {
            return (color & 0x001FU);
        }

        public static int getAverage(short color)
        {
            return (int)((Color.GetRed((ushort)color) << 3) + (Color.GetGreen((ushort)color) << 3) + (Color.GetBlue((ushort)color) << 3)) / 3;
        }
                         
        ushort ToA1R5G5B5() 
        {
            return A8R8G8B8toA1R5G5B5(color); 
        }
                        
        Color GetInterpolated(Color other, float d)
        {
            d = Math.Min(Math.Max(d, 0.0F), 1.0F);                         
            float inv = 1.0F - d;
                         
            return new Color((uint)(other.Alpha * inv + Alpha * d),         
                (uint)(other.Red * inv + Red * d),
                (uint)(other.Green * inv + Green * d),                
                (uint)(other.Blue * inv + Blue * d));                
        }
                  
        Color getInterpolated_quadratic(Color c1, Color c2, float d)     
        {
            // this*(1-d)*(1-d) + 2 * c1 * (1-d) + c2 * d * d;            
            d = Math.Min(Math.Max(d, 0.0F), 1.0F);            
            float inv = 1.0F - d;            
            float mul0 = inv * inv;            
            float mul1 = 2.0F * d * inv;            
            float mul2 = d * d;
             
            return new Color(            
                Math.Min(Math.Max(Convert.ToUInt32(Math.Floor(Alpha * mul0 + c1.Alpha * mul1 + c2.Alpha * mul2)), 0), 255),
                Math.Min(Math.Max(Convert.ToUInt32(Math.Floor(Red * mul0 + c1.Red * mul1 + c2.Red * mul2)), 0), 255),                                       
                Math.Min(Math.Max(Convert.ToUInt32(Math.Floor(Green * mul0 + c1.Green * mul1 + c2.Green * mul2)), 0), 255), 
                Math.Min(Math.Max(Convert.ToUInt32(Math.Floor(Blue  * mul0 + c1.Blue  * mul1 + c2.Blue  * mul2)), 0), 255));
        }

    }
}
